#include "bubles.hpp"
#include <stdlib.h>
#include <math.h>

#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <GL/glext.h>

#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <thread>

extern GLuint load_shaders(const char * vertex, const char * fragment);

buble::buble(float x, float y, float size):
	m_x(x),m_y(y),m_size(size),
	m_hue(0.001f*(random()%1001)),
	m_hit(false)
{
}
buble::~buble()
{
}
bool buble::out_of_screen(falling_things& ctx){
	if(m_hit)return true;//если нас сбили, пусть удаляют
	return !ctx.is_visible(m_x-m_size, m_y-m_size, m_x+m_size, m_y+m_size);
}
void buble::animate(falling_things& ctx, double dt)
{
	//мы тупо падаем...
	m_y-=0.005*m_size*dt;
}
bool buble::hit_test(falling_things& ctx, float x, float y)
{
	//тупо проверяем ткнули ли мы в шарик
	if(!m_hit){
		float dx(m_x-x);
		float dy(m_y-y);
		if(dx*dx+dy*dy<m_size*m_size){
			m_hit=true;
			ctx.add_score(100*(MAX_OBJECT_SIZE/m_size));
			return true;
		}
	}
	return false;
}
/*тормозит!!! да и сферу не так уж сложно отрисовать одним квадратом
static const char my_vertex_shader_source[]={
"varying vec3 normal;\n"
"void main(){\n"
"   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
"   normal = normalize(gl_NormalMatrix * gl_Normal);\n"
"\n"
"}\n"
};
static const char my_fragment_shader_source[]={
"varying vec3 normal;\n"
"uniform vec3 color;\n"
"void main() {\n"
"	float opacity = 0.8*(normal.x*normal.x+normal.y*normal.y);\n"
"   vec3 tmp=color*opacity;\n"
"   gl_FragColor = vec4(tmp, opacity);\n"
"   gl_FragDepth =  gl_FragCoord.z;\n"
"}\n"};
* мы пойдем другим путем: отрисуем квадрат
* я как-то через жопу считаю нормаль
* но т.к. задание тестовое, не будем устраивать тут красоты
* */
static const char my_vertex_shader_source[]={
"varying vec2 normal;\n"
"void main(){\n"
"   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
"   normal = -gl_Vertex.xy;\n"
"\n"
"}\n"
};
//банальный шейдер, следящий, чтоб рисовался шарик
//(для этого мы используем хакнутую нормаль)
//и подкрашивающий правильными цветами пиксели
//чтоб в центре бубль был прозрачнее
static const char my_fragment_shader_source[]={
"varying vec2 normal;\n"
"uniform vec3 color;\n"
"void main() {\n"
"	float opacity = dot(normal,normal);\n"
"   if(opacity>1.0)discard;\n"
"   vec3 tmp=color*opacity;\n"
"   gl_FragColor = vec4(tmp, opacity);\n"
"   gl_FragDepth =  gl_FragCoord.z;\n"
"}\n"};
//единократно создаваемая шейдерная програма
static GLhandleARB shader_program(0);
static std::once_flag stuff_flag;
//static GLuint buble_object(0);
static void load_stuff()
{
//создаем шарег
//===на i915 даже это тормозит===FFFFUUUUUU!!!!
//	buble_object=glGenLists(1);
//	glNewList(buble_object, GL_COMPILE);
//	glutSolidSphere(1.0, 20, 20);
//	glEndList();
	//грузим шейдер
	shader_program=load_shaders(my_vertex_shader_source, my_fragment_shader_source);
}
void buble::paint()
{
	//один раз и навсегда создаем нужные нам хреновинки
	try{
		std::call_once(stuff_flag, load_stuff);
	}catch(std::exception& ex){
		std::cerr<<"failed to load resouces"<<std::endl<<ex.what()<<std::endl;
		exit(1);
	}
	//прячем убитые шарики - их удалят в другом месте
	if(!m_hit){
		//translate coordinates of sphere to specified position
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glTranslatef(m_x, m_y, 0.0f);
		glScalef(m_size, m_size, m_size);
		//attach and configure shader program
		glUseProgram(shader_program);	
		//calculate color of buble
		float r=std::min(fabsf(2*M_PI*m_hue),(float)M_PI/3)*(3/M_PI);
		float g=std::min(fabsf(2*M_PI*m_hue-2*M_PI/3),(float)M_PI/3)*(3/M_PI);
		float b=std::min(fabsf(2*M_PI*m_hue-4*M_PI/3),(float)M_PI/3)*(3/M_PI);
		glUniform3fARB(glGetUniformLocationARB(shader_program, "color"),
					   r, g, b);
		//рисуем квадрат - шейдер сам нарежет из него кружков
		glBegin(GL_QUADS);
		glVertex3f(-1.0f,-1.0f,0.0f);
		glVertex3f(1.0f,-1.0f,0.0f);
		glVertex3f(1.0f,1.0f,0.0f);
		glVertex3f(-1.0f,1.0f,0.0f);
		glEnd();
		//render sphere -- too slow
		//glCallList(buble_object);		
		//revert to fixed function
		glUseProgram(0);	
	}
	//else отрисуем обломки?
}
